package belf.migrate.engine.ddl.view.oracle;

import belf.migrate.api.exception.APIErrorCode;
import belf.migrate.api.exception.CatalogException;
import belf.migrate.api.model.QualifiersModel;
import belf.migrate.api.table.schema.Catalog;
import belf.migrate.engine.ddl.BaseDDLConvert;

import belf.migrate.plugin.oracle.antlr.*;
import com.github.vertical_blank.sqlformatter.SqlFormatter;
import com.github.vertical_blank.sqlformatter.languages.Dialect;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 将Oracle创建视图的DDL转化为达梦的DDL
 */
public class Oracle2DMViewConvert extends BaseDDLConvert {
    private final static String IDENTIFIER_QUOTES = "\""; //达梦数据库的标识符用双引号括起来

    private Map<PlSqlParser.Other_functionContext, TranslateFunction> translateFunctionMap = new HashMap<>();

    public Oracle2DMViewConvert(String objectName, String sql, Catalog sourceCatalog, Catalog sinkCatalog,
                                String schema, QualifiersModel qualifiersModel) {
        super(objectName, sql, sourceCatalog, sinkCatalog, schema, qualifiersModel);
        String taskType = sourceCatalog + "-" + sinkCatalog;
    }

    @Override
    public String convert() {
        Pattern pattern = Pattern.compile("\\sEDITIONABLE\\s", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(sql);
        String replacedSql = matcher.replaceAll(" ");
        CharStream input = CharStreams.fromString(replacedSql);

        // 创建词法分析器和解析器
        PlSqlLexer lexer = new PlSqlLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        PlSqlParser parser = new PlSqlParser(tokens);

        // 从解析器中获取起始规则并进行解析
        PlSqlParser.Sql_scriptContext tree = parser.sql_script();
        OracleViewVistor viewVisitor = new OracleViewVistor();
        // 遍历树找关键节点
        viewVisitor.visit(tree);

        // 重构达梦的视图创建DDL
        return viewVisitor.toDMViewDDL();
    }

    /**
     * 使用antlr4的visitor模式访问Oracle视图的抽象语法树AST，找到重要的节点，
     * 再使用listener模式对这些节点进行遍历，重构达梦数据库的专业视图创建DDL。
     */
    private class OracleViewVistor extends PlSqlParserBaseVisitor<String> {

        private PlSqlParser.Select_only_statementContext selectOnlyStatementContext;
        /** 要创建的视图的名称（不含前缀schema） */
        private String viewName;
        /** 如果视图继承自类型，是哪个类型名称？如果为null，表示是正常的视图，则{@link #columnNames}不能为空 */
        private String typeName;
        /** 视图定义的字段列表 */
        private List<String> columnNames = new ArrayList<>();

        @Override
        public String visitId_expression(PlSqlParser.Id_expressionContext ctx) {
            if (ctx.getParent() instanceof PlSqlParser.Create_viewContext) {
                viewName = ctx.getText();
            } else if (hasAncestor(PlSqlParser.View_alias_constraintContext.class.getName(), ctx)) {//是字段名称
                columnNames.add(ctx.getText());
            } else if (hasAncestor(PlSqlParser.Object_view_clauseContext.class.getName(), ctx)) {//是类型名称
                typeName = ctx.getText();
            }

            return visitChildren(ctx);
        }

        @Override
        public String visitSelect_only_statement(PlSqlParser.Select_only_statementContext ctx) {
            this.selectOnlyStatementContext = ctx;
            return visitChildren(ctx);
        }

        @Override
        public String visitOther_function(PlSqlParser.Other_functionContext ctx) {
            if (ctx.getText().toUpperCase().trim().startsWith("TRANSLATE")) {
                TranslateFunction function = new TranslateFunction();
                translateFunctionMap.put(ctx, function);
            }
            return visitChildren(ctx);
        }

        /**
         * 找函数TRANSLATE的相关信息：是否是编码转换？要被转换的栏目是哪个？
         * @param ctx the parse tree
         * @return
         */
        @Override
        public String visitFunction_argument_modeling(PlSqlParser.Function_argument_modelingContext ctx) {
            ParseTree parent = ctx.getParent();
            if (parent instanceof PlSqlParser.Other_functionContext) {
                PlSqlParser.Other_functionContext otherFunctionContext = (PlSqlParser.Other_functionContext)parent;
                if (translateFunctionMap.containsKey(otherFunctionContext)) {
                    TranslateFunction function = translateFunctionMap.get(otherFunctionContext);
                    for (int i = 0; i < ctx.getChildCount(); i++) {
                        ParseTree child = ctx.getChild(i);
                        if ("USING".equalsIgnoreCase(child.getText().trim())) {
                            function.isEncodingConversion = true;
                        } else if (child instanceof PlSqlParser.Column_nameContext){
                            function.columnName = child.getText().trim();
                        }
                    }
                }
            }
            return visitChildren(ctx);
        }

        public String toDMViewDDL() {
            StringBuilder sb = new StringBuilder();
            sb.append("CREATE OR REPLACE FORCE VIEW "); //注意：FORCE参数，可以无视物理表是否存在，直接创造视图
            if (schema != null) {
                sb.append(IDENTIFIER_QUOTES + schema + IDENTIFIER_QUOTES + ".");
            }
            if (viewName.startsWith("\"")) {
                sb.append(viewName + " ");
            } else {
                sb.append(IDENTIFIER_QUOTES + viewName + IDENTIFIER_QUOTES + " ");
            }

            if (StringUtils.isNotEmpty(typeName)) {
                try {
                    columnNames = sourceCatalog.listTypeColumns(schema, typeName);
                } catch (CatalogException e) {
                }
            }
            if (columnNames != null && !columnNames.isEmpty()) {
                sb.append("(");
                sb.append(String.join(", ", columnNames));
                sb.append(")");
            }
            sb.append(" AS ");
            SelectStatementListener listener = new SelectStatementListener();
            ParseTreeWalker walker = new ParseTreeWalker();
            walker.walk(listener, selectOnlyStatementContext);
            String selectSql = listener.getSelect();
            sb.append(selectSql);

            translateFunctionMap.clear();
            return SqlFormatter.of(Dialect.PlSql).format(sb.toString());
//            return sb.toString();
        }
    }

    private class SelectStatementListener extends PlSqlParserBaseListener {
        List<String> symbols = Arrays.asList("'", "\"", ".", ",", "(", ")", "=", "<", ">");
        private StringBuilder sb = new StringBuilder();

        @Override
        public void visitTerminal(TerminalNode node) { // TODO: 还需要完善函数处理
            String text = node.getText().trim();

            ParseTree parentNode = getParent(PlSqlParser.Tableview_nameContext.class.getName(), node);
            // 1.给表、视图名称强制加上schema前缀
            if (null != parentNode && parentNode instanceof PlSqlParser.Tableview_nameContext) {
                String parentText = parentNode.getText().toUpperCase();
                if (!parentText.startsWith(schema.toUpperCase()) ||
                        parentText.startsWith("\"" + schema.toUpperCase() + "\"")) {
                    sb.append(" " + schema + "." + node.getText());
                    return;
                }
            }

            if ("MAKE_REF".equalsIgnoreCase(text) && hasAncestor("Id_expressionContext", node)) {
                addWarning(new CatalogException(APIErrorCode.CONVERT_VIEW_ERROR, "达梦中没有对应于Oracle的MAKE_REF表达式"));
            }

            if (hasAncestor(PlSqlParser.Other_functionContext.class.getName(), node)) { // 2.处理TRANSLATE
                ParseTree parent = getParent(PlSqlParser.Other_functionContext.class.getName(), node);
                if (parent instanceof PlSqlParser.Other_functionContext) {
                    PlSqlParser.Other_functionContext otherFunctionContext = (PlSqlParser.Other_functionContext) parent;
                    if (translateFunctionMap.containsKey(otherFunctionContext)) {
                        TranslateFunction function = translateFunctionMap.get(otherFunctionContext);
                        if (function != null && function.isEncodingConversion) {//是编码转换函数
                            if (function.used == false) {
                                sb.append(" " + function.columnName);
                                function.used = true;
                            }
                            return;
                        }
                    }
                }
            }

            normarlOutput(node);
        }

        private void normarlOutput(TerminalNode node) {
            String text = node.getText().trim();
            if (symbols.contains(node.getText())) { // 3.处理标点符号，标点符号后面不加空格
                sb.append(text);
            } else {
                sb.append(" " + text);
            }
        }

        public String getSelect() {
            return sb.toString();
        }
    }

    /**
     * 将TRANSLATE"函数当作一个整体输出
     */
    private class TranslateFunction {
        public String name = "TRANSLATE";
        public String columnName = "";
        public boolean isEncodingConversion  = false;
        public boolean used = false;   //是否在输出叶子节点文本时已经用过了
    }

}
